#!/usr/bin/env node

// watchComponents.js 生成路由
// by gjw 2023.8.2

const fs = require('fs')
const path = require('path')

const viewsDir = path.join(__dirname, 'src/components/views')
const routerConfigFile = path.join(__dirname, 'src/router/autoRoutes.ts')

const defaultRoutes = []

let oldRoutes = []

function createRouteConfig (filename, folderPath) {
    const name = path.basename(filename, '.vue')
    const componentFile = path.join(viewsDir, folderPath, `${name}.vue`)

    if (!fs.existsSync(componentFile)) {
        console.warn(`File "${componentFile}" does not exist, skipping create route.`)
        return null
    }

    const fileContent = fs.readFileSync(componentFile, 'utf-8')
    const hasOwnConfig = /<!--\s*ROUTER_OWN_CONFIG\s*-->/.test(fileContent)

    if (hasOwnConfig) return false

    return `${folderPath ? folderPath + '/' : ''}${name}`
}

function traverse (directory, parentPath = '') {
    if (!fs.existsSync(directory)) {
        return []
    }
    const files = fs.readdirSync(directory)
    const routes = files.map(filename => {
        const filePath = path.join(directory, filename)
        const stat = fs.statSync(filePath)
        if (stat.isDirectory()) {
            const folderPath = path.join(parentPath, filename)
            return traverse(filePath, folderPath)
        } else if (stat.isFile() && filename.endsWith('.vue')) {
            return createRouteConfig(filename, parentPath)
        }
  
        return null
    })
  
    return routes.reduce((acc, cur) => acc.concat(cur), [])
}

function compare (arr1, arr2) {
    if (arr1 === arr2) {
        return true
    }
    if (!Array.isArray(arr1) || !Array.isArray(arr2) || arr1.length !== arr2.length) {
        return false
    }
    const sortedArr1 = [...arr1].sort()
    const sortedArr2 = [...arr2].sort()
    for (let i = 0; i < sortedArr1.length; i++) {
        const item1 = sortedArr1[i]
        const item2 = sortedArr2[i]
        if (Array.isArray(item1) && Array.isArray(item2)) {
            if (!compare(item1, item2)) {
                return false
            }
        } else if (typeof item1 === 'object' && typeof item2 === 'object') {
            if (item1 !== null && item2 !== null && item1.constructor === Object && item2.constructor === Object) {
                if (!areObjectsEqual(item1, item2)) {
                    return false
                }
            } else {
                if (item1 !== item2) {
                    return false
                }
            }
        } else if (item1 !== item2) {
            return false
        }
    }   
    return true
}

function updateRoutes (mode = 'run') {
    // return new Promise((resolve, reject) => {
    const routes = traverse(viewsDir).filter(route => route)
    const mergedRoutes = defaultRoutes.concat(routes)
    if (mode === 'latest') {
        return {
            compare,
            latest: mergedRoutes
        }
    }
    const isSame = compare(mergedRoutes, oldRoutes)
    if (isSame) {
        return mode === 'init' ? mergedRoutes : 'noChange'
    }
    console.log('正在生成路由中...')
    oldRoutes = mergedRoutes
    let content = `import { RouteConfig } from 'vue-router'\n\nconst routes: RouteConfig[] = [${mergedRoutes.length ? '\n' : ''}`
    mergedRoutes.reverse().map((v, i) => {
        content += `    {
        path: '${v}',
        name: '${v.replace(/\//g, '_')}',
        component: () => import('@/components/views/${v}.vue')
    }${i === mergedRoutes.length - 1 ? '' : ','}\n`
    })
    content += ']\n\nexport default routes\n'
    fs.writeFileSync(routerConfigFile, content)
    console.log('路由生成完成')
    return mode === 'init' ? mergedRoutes : 'change'
}

if (!fs.existsSync(viewsDir)) {
    fs.mkdirSync(viewsDir)
}

module.exports = updateRoutes

const isExec = process.argv[process.argv.length - 1].slice(2) === 'exec'

if (isExec) {
    function debounce (fn, delay) {
        let timerId
        return function (...args) {
            clearTimeout(timerId)
            timerId = setTimeout(() => {
                fn.apply(this, args)
            }, delay)
        }
    }

    let watcher = require('chokidar').watch(viewsDir).on('all', debounce((event, filename) => {
        updateRoutes()
    }, 10))
    process.on('exit', (c) => {
        watcher.close()
        watcher = null
    })
}
